/*
  Copyright (C) 2011, Kyungjoo Kim
  
  This file is part of LINAL (LINear ALgebra)
  
  All rights reserved.

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.
  
  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Lesser General Public License for more details.
  
  You should have received a copy of the GNU Lesser General Public
  License along with this library; if not, write to the Free Software
  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
  Also add information on how to contact you by electronic and paper mail.
  
  
  Kyungjoo Kim
  iamkyungjoo@gmail.com
*/
#include "linal/common.hxx"
#include "linal/util.hxx"

#include "linal/format.hxx"

#include "linal/device/host/operation.hxx"


namespace linal      {
  namespace host   {
    namespace internal {

      int norm1( FLA_Obj &A, FLA_Obj &norm ) {
        FLA_Norm1(A, norm);
        return true;
      }
      int norm1( int uplo, int diag, FLA_Obj &A, FLA_Obj &norm ) {

        LINAL_ERROR_DETAIL( A.m == A.n,
                            "%s\n %s %d\n %s %d\n",
                            LINAL_ERROR_A_SQUARE_MATRIX,
                            "length = ", A.m,
                            "width  = ", A.n );

        FLA_Obj AL,   AR,       A0,  a1,  A2;

        FLA_Obj b;
        FLA_Obj bL,   bR,       b0,  beta1,  b2;

        if ( FLA_Check_error_level() >= FLA_MIN_ERROR_CHECKING )
          FLA_Norm1_check( A, norm );

        FLA_Obj_create( FLA_Obj_datatype( A ), 1, FLA_Obj_width( A ), 0, 0, &b );

        FLA_Part_1x2( A,    &AL,  &AR,      0, FLA_LEFT );

        FLA_Part_1x2( b,    &bL,  &bR,      0, FLA_LEFT );

        while ( FLA_Obj_width( AL ) < FLA_Obj_width( A ) ){

          FLA_Repart_1x2_to_1x3( AL,   AR,  &A0,  &a1, &A2,
                                 1, FLA_RIGHT );

          FLA_Repart_1x2_to_1x3( bL,   bR,  &b0,  &beta1, &b2,
                                 1, FLA_RIGHT );
          /*------------------------------------------------------------*/

          FLA_Obj a1t, a1b, alpha;

          FLA_Part_2x1( a1, &a1t, 
                            &a1b,
                        FLA_Obj_width( AL ), FLA_TOP );

          FLA_Repart_2x1_to_3x1( a1t, &a1t, 
                                      &alpha, 
                                 a1b, &a1b,
                                 1, FLA_BOTTOM );
          switch (uplo) {
          case FLA_LOWER_TRIANGULAR: 
            FLA_Asum( a1b, beta1 ); 
            break;
          case FLA_UPPER_TRIANGULAR: 
            FLA_Asum( a1t, beta1 ); 
            break;
          }

          switch (diag) {
          case FLA_ZERO_DIAG:    
            break;
          case FLA_UNIT_DIAG:    
            FLA_Mult_add( FLA_ONE, FLA_ONE, beta1 );
            break;
          case FLA_NONUNIT_DIAG:
            FLA_Absolute_value( alpha );
            FLA_Mult_add( FLA_ONE, alpha, beta1 );
            break;
          }

          /*------------------------------------------------------------*/
          FLA_Cont_with_1x3_to_1x2( &AL,   &AR,  A0, a1,  A2,
                                    FLA_LEFT );

          FLA_Cont_with_1x3_to_1x2( &bL,   &bR,  b0, beta1,  b2,
                                    FLA_LEFT );
        }

        FLA_Max_abs_value( b, norm );

        FLA_Obj_free( &b );

        return true;
      }
    }
  }
}
